﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ApprovalFlow.Interface;

namespace ApprovalFlow.Services.BackgroundTasks
{
    public class EventConsumer : IBackgroundTask
    {
        protected Task DispatchTask;
        private CancellationTokenSource _cancellationTokenSource;
        private IQueueProvider _queueProvider;
        private ISubscriptionRepository _subscriptionRepository;
        private IEventRepository _eventRepository;
        private IWorkflowRepository _workflowRepository;
        private IWorkflowExecutor _workflowExecutor;

        protected QueueType Queue => QueueType.Event;

        public EventConsumer(IQueueProvider queueProvider, ISubscriptionRepository subscriptionRepository, IEventRepository eventRepository,
            IWorkflowRepository workflowRepository, IWorkflowExecutor workflowExecutor)
        {
            _queueProvider = queueProvider;
            _subscriptionRepository = subscriptionRepository;
            _eventRepository = eventRepository;
            _workflowRepository = workflowRepository;
            _workflowExecutor = workflowExecutor;
        }

        public void Start()
        {
            if (DispatchTask != null)
            {
                throw new InvalidOperationException();
            }
            _cancellationTokenSource = new CancellationTokenSource();
            DispatchTask = Task.Factory.StartNew(Execute, TaskCreationOptions.LongRunning);
        }

        public void Stop()
        {
            _cancellationTokenSource.Cancel();
            DispatchTask.Wait();
            DispatchTask = null;
        }

        private async Task Execute()
        {
            var cancelToken = _cancellationTokenSource.Token;
            while (!cancelToken.IsCancellationRequested)
            {
                var item = await _queueProvider.DequeueWork(Queue, cancelToken);
                if (item is null)
                {
                    continue;
                }
                var evt = await _eventRepository.GetEvent(item);
                if (evt?.IsProcessed == true)
                {
                    continue;
                }
                var sub = await _subscriptionRepository.GetFirstOpenSubscription(evt.EventName, evt.EventKey);
                if (sub is null)
                {
                    await _eventRepository.MarkEventProcessed(evt.Id);
                    continue;
                }

                var workflow = await _workflowRepository.GetWorkflowInstance(sub.InstanceId);
                var pointers = workflow.ExecutionPointers.Where(p => p.Id == sub.ExecutionPointerId && !p.EventPublished && p.EndTime == null);
                foreach (var p in pointers)
                {
                    p.EventData = evt.EventData;
                    p.EventPublished = true;
                    p.Active = true;
                }
                await _workflowExecutor.Execute(workflow);
            }
        }
    }
}
